<template>
  <el-form
    @submit.prevent
    :model="model"
    class="form-components"
    :class="{ 'block ': !_options.inline, line: _options.option }"
    v-bind="_options"
    ref="formRef"
  >
    <template
      v-for="(item, index) in fieldList"
      :key="`${index}${item.field || '-'}`"
    >
      <FormItem
        :label="item.label"
        v-if="item.type === 'radio'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
        :style="item.full || !_options.inline ? 'flex: 1' : ''"
      >
        <el-radio-group
          v-model="model[item.field]"
          :disabled="item.disabled"
          :style="item.style"
        >
          <el-radio
            v-for="val in getOption(item)"
            :label="val[item.options?.valueKey || 'value']"
            :key="val[item.options?.valueKey || 'value']"
          >
            {{ val[item.options?.labelKey || "label"] }}
          </el-radio>
        </el-radio-group>
        <slot :name="item.field + '-slot'"></slot>
      </FormItem>
      <template
        class="form-title"
        style="width: 100%"
        v-else-if="item.type === 'title'"
      >
        <FormTitle :title="item.label" />
      </template>
      <template v-else-if="item.type === 'br'">
        <div style="width: 100%"></div>
      </template>
      <FormItem
        v-else-if="item.slot"
        :label="item.label"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :style="item.style"
        :show="getShow(item)"
      >
        <FormCombined
          v-if="item.select"
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <slot :name="item.slot" :value="value" :input="input"> </slot>
          </template>
        </FormCombined>
        <slot
          v-else
          :name="item.slot"
          :value="model[item.field]"
          :input="updateSlotValue(item.field)"
        >
        </slot>
      </FormItem>
      <FormItem
        v-else-if="item.type === 'numberRange'"
        :label="item.label"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <NumberRange
          :style="item.style"
          :modelValue="model[item.field]"
          :suffix="item.suffix"
          :precision="item.precision"
          :min="item?.min"
          :max="item?.max"
          :placeholder="item.placeholder"
          @input="updateNumberRange(item.field, $event)"
        />
      </FormItem>
      <FormItem
        :label="item.label"
        v-else-if="item.type === 'checkbox'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <el-checkbox-group
          v-model="model[item.field]"
          :disabled="item.disabled"
          :style="item.style"
        >
          <el-checkbox
            v-for="c in item.options?.data"
            :key="c[item.options?.valueKey || 'value']"
            :label="c[item.options?.valueKey || 'value']"
          >
            {{ c[item.options?.labelKey || "label"] }}
          </el-checkbox>
        </el-checkbox-group>
      </FormItem>
      <FormItem
        :label="item.label"
        v-else-if="item.type === 'select'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
        :style="item.itemStyle"
      >
        <FormCombined
          v-if="item.select"
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <el-select
              :style="[
                item.full || !_options.inline ? { width: '100%' } : null,
                item.style,
              ]"
              :modelValue="value"
              @update:modelValue="input"
              :filterable="item.filterable ?? true"
              :clearable="item.clearable ?? true"
              :popper-append-to-body="false"
              :teleported="false"
              placement="bottom"
              :multiple="item.multiple"
              :collapseTags="item.collapseTags"
              :maxCollapseTags="
                item.collapseTags && !item.maxCollapseTags
                  ? 1
                  : item.maxCollapseTags
              "
              :collapseTagsTooltip="
                item.collapseTags && !item.collapseTagsTooltip
                  ? true
                  : item.collapseTagsTooltip
              "
              :placeholder="item.options?.placeholder || '请选择'"
              :disabled="item.disabled"
              :valueKey="item.valueKey"
              v-bind="item.itemProps"
            >
              <el-option
                v-for="s in getOption(item)"
                :key="s[item.valueKey || item.options?.valueKey || 'value']"
                :label="s[item.options?.labelKey || 'label']"
                :value="
                  item.valueKey ? s : s[item.options?.valueKey || 'value']
                "
              />
            </el-select>
          </template>
        </FormCombined>
        <el-select
          v-else
          :style="[
            item.full || !_options.inline ? { width: '100%' } : null,
            item.style,
          ]"
          v-model="model[item.field]"
          :filterable="item.filterable ?? true"
          :clearable="item.clearable ?? true"
          :popper-append-to-body="false"
          :teleported="false"
          placement="bottom"
          :multiple="item.multiple"
          :collapseTags="item.collapseTags"
          :maxCollapseTags="
            item.collapseTags && !item.maxCollapseTags
              ? 1
              : item.maxCollapseTags
          "
          :collapseTagsTooltip="
            item.collapseTags && !item.collapseTagsTooltip
              ? true
              : item.collapseTagsTooltip
          "
          :placeholder="item.options?.placeholder || '请选择'"
          :disabled="item.disabled"
          :valueKey="item.valueKey"
          :collapse-tags="item.multiple"
          :collapse-tags-tooltip="item.multiple"
          v-bind="item.itemProps"
        >
          <el-option
            label="全选"
            value="all"
            v-if="item?.multiple"
            @click.native="selectAll(item)"
          ></el-option>
          <el-option
            v-for="s in getOption(item)"
            :key="s[item.valueKey || item.options?.valueKey || 'value']"
            :label="s[item.options?.labelKey || 'label']"
            :value="item.valueKey ? s : s[item.options?.valueKey || 'value']"
          />
        </el-select>
        <div v-if="item.default" v-html="item.default"></div>
      </FormItem>
      <FormItem
        :label="item.label"
        v-else-if="item.type === 'dateTimeRange'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <FormCombined
          v-if="item.select"
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <el-date-picker
              :modelValue="value"
              :style="item.style"
              type="datetimerange"
              :format="item.format || 'YYYY-MM-DD HH:mm:ss'"
              :valueFormat="item.format || 'YYYY-MM-DD HH:mm:ss'"
              :shortcuts="item.options?.shortcuts"
              :placeholder="item.options?.placeholder"
              @update:modelValue="input"
              v-bind="item.itemProps"
            />
          </template>
        </FormCombined>
        <el-date-picker
          v-else
          v-model="model[item.field]"
          :style="item.style"
          type="datetimerange"
          :format="item.format || 'YYYY-MM-DD HH:mm:ss'"
          :valueFormat="item.format || 'YYYY-MM-DD HH:mm:ss'"
          :shortcuts="item.options?.shortcuts"
          :placeholder="item.options?.placeholder"
          v-bind="item.itemProps"
        />
      </FormItem>
      <FormItem
        :label="item.label"
        v-else-if="item.type === 'switch'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <el-switch v-model="model[item.field]" v-bind="item.itemProps" />
      </FormItem>
      <FormItem
        :style="item.itemStyle"
        :label="item.label"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
        v-else-if="item.type === 'inputNumber'"
      >
        <FormCombined
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <el-input-number
              :modelValue="value"
              @input="input"
              :style="item.style"
              :readonly="item.readonly"
              :type="item.type ?? 'text'"
              :autosize="item.autosize ?? { minRows: 1, maxRows: 3 }"
              :showPassword="item.showPassword"
              :clearable="item.clearable"
              :placeholder="item.placeholder || `请输入${item.label}`"
              :disabled="item.disabled"
              :controls="item.controls ?? false"
              :min="item.min"
              :max="item.max"
              v-bind="item.itemProps"
            >
              <template #prefix v-if="item.prefix">
                <slot :name="item.prefix"></slot>
              </template>
              <template #append v-if="item.append && slots[item.append]">
                <slot :name="item.append"></slot>
              </template>
              <template #append v-if="item.append && !slots[item.append]">
                {{ item.append }}
              </template>
            </el-input-number>
          </template>
        </FormCombined>
        <slot :name="item.field + '-slot'"></slot>
        <div v-if="item.default" v-html="item.default"></div>
      </FormItem>
      <FormItem
        :style="item.itemStyle"
        :label="item.label"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
        v-else
      >
        <FormCombined
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <el-input
              :modelValue="value"
              @input="input"
              :style="item.style"
              :readonly="item.readonly"
              :type="item.type ?? 'text'"
              :autosize="item.autosize ?? { minRows: 1, maxRows: 3 }"
              :showPassword="item.showPassword"
              :clearable="item.clearable"
              :placeholder="item.placeholder || `请输入${item.label}`"
              :disabled="item.disabled"
              v-bind="item.itemProps"
            >
              <template #prefix v-if="item.prefix">
                <slot :name="item.prefix"></slot>
              </template>
              <template #append v-if="item.append && slots[item.append]">
                <slot :name="item.append"></slot>
              </template>
              <template #append v-if="item.append && !slots[item.append]">
                {{ item.append }}
              </template>
            </el-input>
          </template>
        </FormCombined>
        <slot :name="item.field + '-slot'"></slot>
        <div v-if="item.default" v-html="item.default"></div>
      </FormItem>
    </template>
    <div
      v-if="_options.option"
      class="ml-[auto] pb-[16px]"
      :class="{
        'flex justify-between w-[100%]': optionFull || slots.buttonRight,
      }"
    >
      <div></div>
      <div class="flex items-center">
        <slot name="buttons" :model="model" :formRef="formRef">
          <slot name="buttonLeft"></slot>
          <el-button v-if="_options.showResetButton" @click="resetForm">
            {{ _options.resetButtonText }}
          </el-button>
          <el-button v-if="_options.showCancelButton" @click="emit('cancel')">
            {{ _options.cancelButtonText }}
          </el-button>
          <el-button
            :class="{ 'w-full': options?.blockSubmitButton }"
            type="primary"
            @click="onSubmit"
            >{{ _options.submitButtonText }}
          </el-button>
          <slot name="buttonRight"></slot>
        </slot>
      </div>
    </div>
  </el-form>
</template>
<script setup>
import FormTitle from "@/components/Form/FormTitle.vue";
import FormItem from "@/components/Form/FormItem.vue";
import FormCombined from "@/components/Form/FormCombined";
import NumberRange from "@/components/NumberRange/index.vue";

import { overallOptions } from "@/api/public";

import { strHump } from "@/utils/index";

const slots = useSlots();

const model = ref({});
const formRef = ref();
const originOptions = ref({});
const props = defineProps({
  fieldList: {
    /**
     * {
        label?: string,
        labelWidth?: string | number, // 标签宽度，例如 '50px'。 可以使用 auto。
        field: string,
        type?: ItemType,
        value?: any,
        placeholder?: string,
        disabled?: boolean,
        readonly?: boolean,
        options?: IFieldOptions,
        rules?: import('element-plus').FormItemRule[]
        clearable?: boolean // 是否可清空
        showPassword?: boolean, // 是否显示切换密码图标
        enterable?: boolean, // 当为输入框时，是否启用回车触发提交功能
      }[]
     */
    type: Array,
    default: () => [],
  },
  model: {
    type: Object,
    default: () => ({}),
  },
  options: {
    /**
      * {
        labelWidth?: string | number,
        labelPosition?: 'left' | 'right' | 'top',
        disabled?: boolean,
        size?: 'large' | 'small' | 'default',
        showResetButton?: boolean, // 是否展示重置按钮
        showCancelButton?: boolean, // 是否展示取消按钮
        submitButtonText?: string,
        resetButtonText?: string,
        cancelButtonText?: string,
        blockSubmitButton?: boolean // 提交按钮是否以块级按钮呈现
    }
   */
    type: Object,
    default: () => ({}),
  },
  optionFull: { type: Boolean, default: false },
  originOption: { type: Object, default: () => ({}) },
});

const emit = defineEmits(["submit", "cancel", "reset", "change","optionsData"]);

const _options = computed(() => {
  const option = {
    // labelWidth: 'auto',
    labelPosition: "right",
    disabled: false,
    scrollToError: true,
    scrollIntoViewOptions: true,
    submitButtonText: "查询",
    resetButtonText: "重置",
    cancelButtonText: "取消",
    line: true,
  };
  return Object.assign(option, props?.options);
});

const updateSlotValue = (field) => (e) => {
  model.value[field] = e;
};

const handleCombined = (item, e) => {
  const { key, value } = e || {};
  if (item.select) {
    item.select.forEach((n) => {
      model.value[n.value] = undefined;
    });
    if (!key) return;
    model.value[key] = value;
    model.value[item.field] = { key, value };
  } else {
    model.value[item.field] = value ? JSON.parse(JSON.stringify(value)) : value;
  }
};

const getOption = (item) => {
  const { originOption } = props;
  return (
    originOption[item.originField] ||
    originOptions.value?.[strHump(item.origin)] ||
    item.options?.data
  );
};

const updateNumberRange = (field, val) => {
  model.value[field] = val;
};

const getShow = (row) => {
  const { show = () => true } = row;
  return show ? show?.({ row, model: model.value }) : true;
};

const handleGetOriginOptions = async (originArr) => {
  try {
    const optionsParams = originArr.map((n) => n.origin);
    try {
      const res = await overallOptions({
        params: optionsParams,
        option: "new",
      });
      const { data } = res;
      for (let item in data) {
        originOptions.value[item] = data[item];
      }
      emit("optionsData", originOptions.value);
    } catch (e) {
      console.error(e);
    }
  } catch (e) {
    console.error(e);
  }
};

const handleDefault = () => {
  props.fieldList.map((item) => {
    // 如果类型为checkbox，默认值需要设置一个空数组
    const value = item.type === "checkbox" ? [] : "";
    props.model
      ? (model.value = JSON.parse(JSON.stringify(props.model)))
      : (model.value[item.field] = item.value || value);
  });
};
handleDefault();

//全选
const selectAll = (val) => {
  let data = getOption(val);
  if (model.value[val.field]?.length < data?.length) {
    model.value[val.field] = [];
    data?.map((item) => {
      model.value[val.field].push(
        item[val.valueKey || val.options?.valueKey || "value"]
      );
    });
    model.value[val.field] = model.value[val.field]?.filter(
      (item) => item != "all"
    );
  } else {
    // 取消全选
    model.value[val.field] = [];
  }
};
watch(
  () => model.value,
  (val) => {
    emit("change", val);
  },
  { deep: true }
);

watch(
  () => props.model,
  (val) => {
    if (val && Object.keys(val).length) {
      handleDefault();
    }
  },
  { deep: true }
);

watch(
  () => props.fieldList,
  () => {
    const originArr = props.fieldList.filter((n) => n.origin);
    if (originArr?.length) {
      handleGetOriginOptions(originArr);
    }
  },
  { immediate: true, deep: true }
);

const onSubmit = () => {
  formRef.value.validate((valid) => {
    const _model = JSON.parse(JSON.stringify(model.value));
    if (valid) {
      emit("submit", _model);
      return false;
    }
  });
};

const submit = () => {
  return new Promise((resolve, reject) => {
    formRef.value.validate((valid) => {
      const _model = JSON.parse(JSON.stringify(model.value));
      if (valid) {
        resolve(_model);
      } else {
        reject(false);
      }
    });
  });
};

const getFormValues = () => {
  const _model = JSON.parse(JSON.stringify(model.value));
  return _model;
};

const handleKeyUp = (enterable) => {
  if (!enterable) return;
  onSubmit(formRef.value);
};

const resetForm = () => {
  model.value = JSON.parse(JSON.stringify(props.model));
  formRef.value.resetFields();
  emit("reset");
};

defineExpose({
  formRef,
  onSubmit,
  resetForm,
  submit,
  getFormValues,
});
</script>
<style lang="scss" scoped>
.form-components {
  display: flex;
  flex-wrap: wrap;
  &.line {
    border-bottom: 1px solid #eff3fe;
  }

  :deep(.el-form-item--default) {
    margin-bottom: 8px;
    margin-right: 8px;
  }
  :deep(.el-input-group) {
    .el-input__wrapper {
      border-top-right-radius: 0;
      border-bottom-right-radius: 0;
    }
  }
}
</style>
